#include <kernel.h>
#include <pa4.h>
#include <buf.h>


extern int dskread(struct devsw *pdev, char *buffer, int block_no, int count);
extern int dskwrite(struct devsw *pdev, char *buffer, int block_no, int count);
extern int memncpy(void *dest, void *src, int num);
extern currpid;
/*
 * Part A 3/4. buf_fetch()
 * buf_fetch() does:
 *     if there is a buffer that already store the block data of block_no,
 *         then return the buffer.
 *     otherwise
 *         read the block of block_no, store it on a buffer, and return the buffer.
 * 
 * parameters:
 *     pdev:		device descriptor
 *     block_no: 	a block number to read
 *     policy: 		buffer replacement policy
 */

void evict(int count)
{
int i;
dsk_buffer_p penultimate,tail;
	
	for(i=0;i<count;i++)
	{
	
	penultimate=tail=buf_head;
		while(tail->next!= NULL)
		{
			penultimate =tail;
			tail=tail->next;
		}	
	penultimate->next=NULL;

	if(tail->dirty == 1)	
		{
			dskwrite(tail->pdev,tail->data,tail->block_no,1);
		}
	//freemem(tail->data,128);
	freemem(tail,sizeof(dsk_buffer_p));
	buf_count--;
	
	}
	

}


dsk_buffer_p findNode(int block_no,struct devsw *pdev)
{

dsk_buffer_p temp;

temp = buf_head;
	while(temp != NULL)
	{
		if((temp->block_no == block_no)&&(temp->pdev==pdev)) 
			break;			
		temp=temp->next;
	}
	return temp;
}


dsk_buffer_p readAndInsert(struct devsw *pdev,int block_no)
{

int i;
dsk_buffer_p blk,temp;
int count;
char *buffer;	//char or word

count=buf_count+1+PA4_PREFETCH_SIZE-PA4_BUFFER_SIZE;
if (count > 0)
	evict(count);

buffer=getmem(128*(PA4_PREFETCH_SIZE+1));

dskread(pdev,buffer,block_no,PA4_PREFETCH_SIZE+1);	

	for(i=0;i<PA4_PREFETCH_SIZE+1;i++)
	{
		if((findNode(block_no+i,pdev))== NULL)
			{
					if(buf_count==PA4_BUFFER_SIZE)
					evict(1);
					temp=getmem(sizeof(struct buf));
					temp->block_no = block_no+i;
					temp->pdev = pdev;
					temp->size = 128;
					temp->data = getmem(128);
					memncpy(temp->data,buffer+(i*128),128);
					temp->next = buf_head;
					temp->dirty=0;
					buf_head = temp;
					buf_count++;
					if (i==0)
						blk = temp;	


			}


	}
return blk;
}

void modeToHead(int block_no,struct devsw *pdev)
{
	dsk_buffer_p temp,prev;

	if((buf_head->block_no == block_no)&&(buf_head->pdev==pdev))
		return;
	else
	{
	temp=buf_head;
	while(temp!=NULL)
		{
			if((temp->block_no==block_no)&&(temp->pdev==pdev))
			break;	
			prev=temp;
			temp=temp->next;
		}
	prev->next=temp->next;
	temp->next=buf_head;
	buf_head=temp;
	return;
	}
}

dsk_buffer_p buf_fetch(struct devsw *pdev, int block_no, int policy) 
{
	STATWORD ps;
	dsk_buffer_p buffer;
	int count=0;
	
	if(pdev==&devtab[DISK0])
	{
		while(semaphoredsk0[block_no] == 1)
		{
		//kprintf("\t1.Block %d already requested,suspending process till the block is fetched\n",block_no);
		count++;
		resched();
		}
		semaphoredsk0[block_no] = 1;
		//kprintf("\nDISK 1 Block %d Requested and set to %d by PID %d",block_no,semaphoredsk0[block_no],currpid);
	}
	else
	{
		while(semaphoredsk1[block_no] == 1)
		{
		//kprintf("\t2.Block %d already requested,suspending process till the block is fetched\n",block_no);
		count++;
		resched();
		}
		semaphoredsk1[block_no] = 1;
		//kprintf("\nDISK 2 Block %d Requested and set to %d by PID %d",block_no,semaphoredsk1[block_no],currpid);

	}
	
	buffer=findNode(block_no,pdev);

	if(buffer==NULL)
	{
		buffer = readAndInsert(pdev,block_no);
		if(pdev==&devtab[DISK0])
		semaphoredsk0[block_no] = 0;
		else
		semaphoredsk1[block_no] = 0;
		return buffer; 
		
	}
	else
	{
			if(policy == POLICY_LRU)
				modeToHead(block_no,pdev);	//update the head of the linked list
	
		if(pdev==&devtab[DISK0])
		{
		semaphoredsk0[block_no]=0;
		//kprintf("\nDISK 1 Block %d set to %d by PID %d",block_no,semaphoredsk0[block_no],currpid);
		}
		else
		{
		semaphoredsk1[block_no]=0;
		//kprintf("\nDISK 2 Block %d set to %d by PID %d",block_no,semaphoredsk1[block_no],currpid);
		}
		return buffer;
	}

	
}

